package com.sojson.config.task;

import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import com.sojson.config.task.bean.CustomTaskDecorator;
import com.sojson.config.task.bean.TdHandler;
import com.sojson.util.ThreadUtil;

/**
 * 定时任务线程池配置类
 *
 * @author liu
 * @date 2021-04-28
 */
@Configuration
/**开启异步调用*/
@EnableAsync
/** 开启定时任务 */
//@EnableScheduling
public class TaskPoolConfig {

    public static final String KEY_THREAD = "THREAD-";
    public static final String KEY_TASK = "TASK-";

    /**
     * 名称taskExecutor为普通线程的默认线程池(springboot会去找这个名字的线程池)
     * 并行任务使用策略: 多线程处理<BR/>
     * destroyMethod = "shutdown"(销毁Bean的时候执行的回调方法,猜是反射机制)
     *
     * @return
     */
    @Bean(destroyMethod = "shutdown")
    public ThreadPoolTaskExecutor taskExecutor(List<TdHandler<?>> tdHandlers) {
        // 创建一个线程池调度器,ThreadPoolTaskExecutor用来处理普通任务
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数,线程池的最小线程数.即使没有任务需要执行,线程池也会保持这个数量的活动线程
        executor.setCorePoolSize(40);
        // 最大线程数,线程池的最大线程数量.当队列已满且活动线程数达到核心线程数时,线程池会创建新线程,直到达到最大线程数
        executor.setMaxPoolSize(200);
        // 任务队列容量,用于存放等待执行的任务.超出这个容量的任务会被拒绝执行,除非线程池已满
        executor.setQueueCapacity(1000);
        // 线程空闲后的最大存活时间(单位: 秒)
        executor.setKeepAliveSeconds(60);
        // 守护线程模式,主进程退出,子线程一起退出
        executor.setDaemon(true);
        /**
         * 当任务数量超过MaxPoolSize和QueueCapacity时使用的策略,该策略是又调用任务的线程执行</BR>
         * AbortPolicy:         默认策略,直接抛出异常,不再执行
         * CallerRunsPolicy:    简单点说就是后面排队的线程就在那儿等着
         *                      被拒绝的任务在主线程中运行,所以主线程就被阻塞了,别的任务只能在被拒绝的任务执行完之后才会继续被提交到线程池执行
         * DiscardPolicy:       直接丢弃此线程,不会抛异常也不会执行
         * DiscardOldestPolicy: 会抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务添加进去(不管之前的任务有没有执行结束)
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 设置线程池的工厂,类似切面(创建线程时执行)
        executor.setThreadFactory(
            ThreadUtil.getThreadFactoryNotNum(KEY_THREAD, executor.getCorePoolSize(), executor.getMaxPoolSize()));
        // 设置线程池的装饰器,类似切面(运行线程时执行)
        executor.setTaskDecorator(new CustomTaskDecorator(tdHandlers));
        // 初始化
        executor.initialize();
        return executor;
    }

    /**
     * 并行任务使用策略: 多线程处理<BR/>
     * destroyMethod = "shutdown"(销毁Bean的时候执行的回调方法,猜是反射机制)
     *
     * @return
     */
    @Bean(destroyMethod = "shutdown")
    public ThreadPoolTaskScheduler taskSchedulerDefault() {
        // 创建一个线程池调度器,ThreadPoolTaskScheduler专门用来处理定时任务
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        // 设置线程池容量
        scheduler.setPoolSize(40);
        // 守护线程模式,主进程退出,子线程一起退出
        scheduler.setDaemon(true);
        // 任务被取消时从线程池移除
        scheduler.setRemoveOnCancelPolicy(true);
        // 设置线程池的工厂,类似切面(创建线程时执行)
        scheduler.setThreadFactory(ThreadUtil.getThreadFactory(KEY_TASK));
        return scheduler;
    }

}